﻿#include "stdafx.h"
#include "qg/qgasv.h"
#include "qg/qgimage.h"
#include "qg_trace.h"
#include "qg_image_work.h"
#include "extend/sbvalues.h"

//////////////////////////////////////////////////////////////////////////
// 디바이스
KIMPL_SUBCLASS_ABSTRACT(qgDevice, "RenderDevice");

#if _QG_USE_DX9
namespace _dx9
{
	extern kType DevTypeDX9();
}
#endif

#if _QG_USE_D11
namespace _d11
{
	extern kType DevTypeD11();
}
#endif

#if _QG_USE_GL
namespace _ogl
{
	extern kType DevTypeGL();
}
#endif

#if _QG_USE_ES2
namespace _es2
{
	extern kType DevTypeES2();
}
#endif

namespace _supp
{
	// 디바이스 타입 획득
	static kType DetectDevType(const char* text, kint* flags)
	{
		static struct
		{
			const char* alias1;
			const char* alias2;
			const char* alias3;
			kType(*func)();
			kint flags;
			kint ossub;
		} lsts[] =
		{
#if _QG_USE_D11
			{"D11", "DX11", "DirectX11", &_d11::DevTypeD11, QGSDF_BGRA, KSIPFS_WINDOWS_8},
#endif
#if _QG_USE_DX9
			{"DX", "DX9", "DirectX9", &_dx9::DevTypeDX9, 0, 0},
#endif
#if _QG_USE_GL
			{"GL", "OGL", "OPENGL", &_ogl::DevTypeGL, QGSDF_INDP, 0},
#endif
#if _QG_USE_ES2
			{"ES2", "GLES2", "OPENGLES2", &_es2::DevTypeES2, 0, 0},
#endif
			{"", "", "", NULL, 0, 0},
		};

		kint ossub = k_sisd_ossub();

		if (!text || !*text)
		{
			for (ksize_t i = 0; i < K_COUNTOF(lsts); i++)
			{
				if (lsts[i].func && ossub >= lsts[i].ossub)
				{
					*flags |= lsts[i].flags;
					return lsts[i].func();
				}
			}
		}
		else
		{
			for (ksize_t i = 0; i < K_COUNTOF(lsts); i++)
			{
				if ((k_strieqv(lsts[i].alias1, text) ||
					k_strieqv(lsts[i].alias2, text) ||
					k_strieqv(lsts[i].alias3, text)) &&
					ossub >= lsts[i].ossub)
				{
					*flags |= lsts[i].flags;
					return lsts[i].func();
				}
			}
		}

		return KTYPE_NULL;
	}

	// 스터브용 디바이스
	class SevDevice : public qgDevice
	{
	public:
		bool _sev_layout(qgSevLayout& e)
		{
			Layout(e);

			return true;
		}

	public:
		void _register_sev(qgStub* stub)
		{
			_sevlo = stub->AddLayoutHandler(qgSevLayoutFunc(this, &SevDevice::_sev_layout));
		}

		void _unregister_sev(qgStub* stub)
		{
			if (!stub)
				return;

			stub->RemoveHandler(_sevlo);
		}
	};
}

//
qgDevice* qgDevice::CurrentDevice = NULL;

//
qgDevice::qgDevice()
: _dlock()
, _mlock()
{
	if (CurrentDevice == NULL)
		CurrentDevice = this;

	// nfo 
	// : 초기화 필요없음

	// caps
	// : 초기화 필요없음

	// TM 초기화
	for (ksize_t i = 0; i < QGTMS_MAX_VALUE; i++)
		_trfm.tm[i].Identify();

	_trfm.vipr.Identify();
	_trfm.invv.Identify();

	_trfm.otm.Identify();
	_trfm.tfm.Identify();

	// 컨테이너
	_ctnr = new qgDevContainer();
}

//
qgDevice::~qgDevice()
{
	if (CurrentDevice == this)
		CurrentDevice = NULL;

	delete _ctnr;

	kobj_unload(_mount);
}

//
const char* qgDevice::GetDeviceName()
{
	return type_info->name;
}

//
bool qgDevice::_Initialize(qgStub* stub, kint flags)
{
	// 이 함수는 post initialize 역할을 함

	// 스터브 가지고 있기
	_stub = stub;
	stub->Load();

	// 스터브 등록
	static_cast<_supp::SevDevice*>(this)->_register_sev(stub);

	// 스터브에 읽기 등록
	stub->RegisterReader(QGSLCS_1, _img::CreateImageBmpReader());
	stub->RegisterReader(QGSLCS_1, _img::CreateImagePngLoader());
	stub->RegisterReader(QGSLCS_1, _img::CreateImageJpegLoader());

	// 스터브에 쓰기 등록
	stub->RegisterWriter(_img::CreateImageBmpWriter());
	stub->RegisterWriter(_img::CreateImageTgaWriter());
	stub->RegisterWriter(_img::CreateImagePngWriter());
	stub->RegisterWriter(_img::CreateImageJpegWriter());

	// 준비하기
	return _ResetPrepare(stub->GetSize(), true);
}

//
bool qgDevice::_ResetPrepare(const tpl::Point& size, bool prepare)
{
	ksize_t i;

	// 이 함수는 reset 역할을 하나
	// 제일 처음에는 prepare=true로 preparation 역할도 함

	// 하부 데이터 확인
	if (!_sprn || !_sdeb)
	{
		// 위의 두개는 상속 받은 _ResetPrepare()에서 만들어야 한다
		kobj_tracet(KLV_FATAL, KERR_ARGUMENT, "no render relative data found!!!");
		return false;
	}

	// 상태 처리
	_spcf.draw_2d_auto_set = true;

	// TM처리, 여기서는 화면 크기와 뷰포트만 처리함
	_trfm.sd = size;

	_trfm.vp.loc = tpl::Point(0, 0);
	_trfm.vp.size = size;
	_trfm.vp.depth = tpl::Vec2(0.0f, 1.0f);
	_trfm.vp.aspect = size.Aspect();

	_vsize.Set((float)size.x, (float)size.y);

	// 상태값 초기화

	// 칠하기
	_sprn->cr_bg.Set(0.4f, 0.4f, 0.4f);

	// 광원 
	_sprn->l_ambi.Set(0.1f, 0.1f, 0.1f);
	_sprn->l_fill.Set(0.0f, 0.0f, 0.0f);
	_sprn->l_sop = 0;

	// 재질 알파 연산 및 테스트
	_sprn->m_aop = QGCOP_GR;
	_sprn->m_aref = 0.0f;

	// 재질 샘플러 & 텍스쳐 & 팩터
	for (i = 0; i < QGSGTEX_MAX_VALUE; i++)
	{
		_sprn->m_factor[i].p = NULL;
		_sprn->m_tex[0] = NULL;
		_sprn->m_splr[0] = NULL;
	}

	// 재질 그 밖
	_sprn->m_rasz = NULL;
	_sprn->m_blnd = NULL;
	_sprn->m_shd = NULL;

	// 오프
	_sprn->mo_cnt = 0;

	for (i = 0; i < QGSGOFF_MAX_VALUE; i++)
		_sprn->mo_tex[i] = NULL;

	// 그리기
	_sprn->w_tpg = QGTPG_NONE;
	_sprn->w_ioff = 0;
	_sprn->w_lo = NULL;
	_sprn->w_ib = NULL;
	_sprn->w_vb[0] = NULL;
	_sprn->w_vb[1] = NULL;
	_sprn->w_vb[2] = NULL;
	_sprn->w_vb[3] = NULL;

	// 뼈대
	_sprn->wb_cnt = 0;
	_sprn->wb_pcb = NULL;

	// 바인드
	for (i = 0; i < QGSGTEX_MAX_VALUE; i++)
		_sprn->bd_splr[i] = false;

	_sprn->bd_rasz = false;
	_sprn->bd_blnd = false;

	// 인수 prepare에 해당하는거 만들어야 하네... 
	if (prepare)
	{
		// 각각, 블렌드, 래스터라이저, 샘플러
		_sdeb->blnd = CreateBlnd(qgBlndDataEx(QGBLO_ADD, QGBLV_SA, QGBLV_SA_INV, QGBLO_ADD, QGBLV_SA, QGBLV_SA_INV));
		_sdeb->bglyph = CreateBlnd(qgBlndDataEx(QGBLO_ADD, QGBLV_ONE, QGBLV_SA_INV, QGBLO_ADD, QGBLV_ONE, QGBLV_ZERO));
		_sdeb->rasz = CreateRasz(qgRaszDataEx(QGCOP_NONE, false));
		_sdeb->splr = CreateSplr(qgSplrDataEx(QGFLT_MAG_MIN_MIP_POINT));
		_sdeb->slinear = CreateSplr(qgSplrDataEx(QGFLT_MAG_MIN_MIP_LINEAR));
	}

	// 설정없이 그려도 되도록 미리 설정함
	SetRasz(_sdeb->rasz);
	SetBlnd(_sdeb->blnd);
	SetSplr(0, _sdeb->splr);	// 하나만 해도 될까?

	return true;
}

//
void qgDevice::_Finalize()
{
	// post finalize 임
	ksize_t i;

	// debz
	if (_sdeb != NULL)
	{
		kobj_unload(_sdeb->rasz);
		kobj_unload(_sdeb->blnd);
		kobj_unload(_sdeb->bglyph);
		kobj_unload(_sdeb->splr);
		kobj_unload(_sdeb->slinear);

		// prn
		for (i = 0; i < QGSGTEX_MAX_VALUE; i++)
		{
			kobj_unload(_sprn->m_tex[i]);
			kobj_unload(_sprn->m_splr[i]);
		}
		kobj_unload(_sprn->m_rasz);
		kobj_unload(_sprn->m_blnd);
		kobj_unload(_sprn->m_shd);

		for (i = 0; i < QGSGOFF_MAX_VALUE; i++)
			kobj_unload(_sprn->mo_tex[i]);

		for (i = 0; i < QGSGVERT_MAX_VALUE; i++)
			kobj_unload(_sprn->w_vb[i]);

		kobj_unload(_sprn->w_ib);
		kobj_unload(_sprn->w_lo);
	}

	// 등록 해제
	static_cast<_supp::SevDevice*>(this)->_unregister_sev(_stub);

	// 컨테이너 해제
	_ctnr->Dispose(true);

	// 스텁
	kobj_unload(_stub);
}

//
bool qgDevice::_RoamLoad(qgRoam roam, qgObj* node)
{
	kGuardSpinLock(_mlock)
	{
		if (!_ctnr->Add(roam, node))
			return false;
	}

	node->_roam = roam;

	return true;
}

//
bool qgDevice::_RoamUnload(qgRoam roam, qgObj* node)
{
	k_return_value_if_fail(_ctnr->roam != roam, false);

	kGuardSpinLock(_mlock)
	{
		if (!_ctnr->Remove(roam, node))
			return false;
	}

	node->_roam = QGROAM_DSV;

	return true;
}

//
qgObj* qgDevice::_RoamFind(qgRoam roam, kconstpointer key)
{
	qgObj* obj = NULL;

	kGuardSpinLock(_mlock)
		obj = _ctnr->Find(roam, key);

	return obj;
}

//
void qgDevice::_RoamSignal(const char* signal, kpointer argdata)
{
	kGuardSpinLock(_mlock)
		_ctnr->Signal(signal, argdata);
}

//
bool qgDevice::Layout(const qgSevLayout& e)
{
	QGTRACE_DEVICE(qgDevice);

	kGuardSpinLock(_dlock)
	{
		if (_trfm.sd.width == (kuint)e.width && _trfm.sd.height == (kuint)e.height)
		{
			// 자료를 변경할 필요가 없음
			return false;
		}

		_trfm.sd.Set(e.width, e.height);

		// 오프 스크린 있으면 뷰포트 내비둠
		if (_sprn->mo_cnt > 0)
			return true;

		// 뷰포트를 풀 스크린을 바꿈
		_trfm.vp.loc.Set(0, 0);
		_trfm.vp.size.Set(e.width, e.height);
		_trfm.vp.depth.Set(0.0f, 1.0f);
		_trfm.vp.aspect = _trfm.vp.size.Aspect();

		// 그리기 관련 정보 변경
		_vsize.Set((float)e.width, (float)e.height);
	}

	return true;
}

//
bool qgDevice::Run()
{
	QGTRACE_DEVICE(qgDevice);

	if (!_stub->Run())
		return false;
	else
	{
		if (_stub->GetInsts(QGSDI_VIRTUAL))
		{
			// 가상 모드일 때 디바이스 크기 확인
			_stub->Layout();
		}

		return true;
	}
}

//
kint qgDevice::Mount(const char* path, bool autoarchive)
{
	kint ret = -1;

	kGuardSpinLock(_mlock)
	{
		kobj_reset(&_mount);

		char* ext = path ? k_strrchr(path, '.') : NULL;

		if (!ext)
		{
			_mount = kFuse::New(path);

			if (_mount)
			{
				if (!autoarchive)
					ret = 0;
				else
				{
					// 오토 마운트는 해당 경로의 모든 hfs를 가져오는 것임
					ret = ((kFuse*)_mount)->AutoMount();
				}
			}
		}
		else
		{
			tpl::BlueStr<64> bs(ext + 1);

			if (bs.CompareTo("hfs", true))
				_mount = kHfs::New(path);
			else
			{
				// 헐...
			}

			if (_mount)
				ret = 0;
		}
	}

	return ret;
}

void qgDevice::ActiveDevice(qgDevice* dev)
{
	CurrentDevice = dev;
}

//
qgDevice* qgDevice::New(const char* dev, const tpl::Rect& bound, kint flags /*= 0*/)
{
	//
	kType type = _supp::DetectDevType(dev, &flags);
	if (type == KTYPE_NULL)
	{
		k_tracet(KLV_ERROR, kTsb(Type()).GetName(), KERR_NOTYPE, "no valid renderer device found");
		return NULL;
	}

	qgDevice* self = (qgDevice*)kCrbs::Create(type);
	if (!self)
	{
		// 이유는 create 내부에서 표시해 줄것임
		return NULL;
	}

	//
	qgStub* stub = qgStub::New(bound, NULL, flags);

	if (!self->_Initialize(stub, flags))
	{
		self->Unload();
		self = NULL;
	}

	stub->Unload();

	return self;
}

//
qgDevice* qgDevice::New(const char* dev, kint width, kint height, kint flags /*= 0*/)
{
	//
	kType type = _supp::DetectDevType(dev, &flags);
	if (type == KTYPE_NULL)
	{
		k_tracet(KLV_ERROR, kTsb(Type()).GetName(), KERR_NOTYPE, "no valid renderer device found");
		return NULL;
	}

	qgDevice* self = (qgDevice*)kCrbs::Create(type);
	if (!self)
	{
		// 이유는 create 내부에서 표시해 줄것임
		return NULL;
	}

	//
	qgStub* stub = qgStub::New(width, height, NULL, flags);

	if (!self->_Initialize(stub, flags))
	{
		self->Unload();
		self = NULL;
	}

	stub->Unload();

	return self;
}

//
qgDevice* qgDevice::NewVirtual(const char* dev, kpointer handle, kint flags /*= 0*/)
{
	//
	kType type = _supp::DetectDevType(dev, &flags);
	if (type == KTYPE_NULL)
	{
		k_tracet(KLV_ERROR, kTsb(Type()).GetName(), KERR_NOTYPE, "no valid renderer device found");
		return NULL;
	}

	qgDevice* self = (qgDevice*)kCrbs::Create(type);
	if (!self)
	{
		// 이유는 create 내부에서 표시해 줄것임
		return NULL;
	}

	//
	qgStub* stub = qgStub::NewVirtual(handle);

	if (!self->_Initialize(stub, flags))
	{
		self->Unload();
		self = NULL;
	}

	stub->Unload();

	return self;
}

//////////////////////////////////////////////////////////////////////////
// 본격 오버로딩 함수

//
bool qgDevice::Enter(kint clearance /*= 0xFF*/)
{
	QGTRACE_DEVICE(qgDevice);

	_nfo.flush = false;

	return true;
}

//
bool qgDevice::Leave()
{
	QGTRACE_DEVICE(qgDevice);

	_nfo.flush = true;

	return true;
}

//
bool qgDevice::Flush()
{
	QGTRACE_DEVICE(qgDevice);

	_nfo.frames++;

	return true;
}

//
void qgDevice::SetBackground(const tpl::ColorValue& color)
{
	QGTRACE_DEVICE(qgDevice);

	kGuardSpinLock(_dlock)
		_sprn->cr_bg = tpl::Color(color);
}

//
void qgDevice::SetTexture(kint stage, qgTexture* tex)
{
	QGTRACE_DEVICE(qgDevice);

	k_return_if_fail((ksize_t)stage < (ksize_t)_caps.max_texture_count);

	kGuardSpinLock(_dlock)
	{
		if (_sprn->m_tex[stage] != tex)
		{
			kobj_unload(_sprn->m_tex[stage]);
			kobj_load(tex);

			_sprn->m_tex[stage] = tex;
			_sprn->bd_tex[stage] = true;
		}
	}
}

//
void qgDevice::SetRasz(qgRasz* rasz)
{
	QGTRACE_DEVICE(qgDevice);

	kGuardSpinLock(_dlock)
	{
		if (_sprn->m_rasz != rasz)
		{
			kobj_unload(_sprn->m_rasz);
			kobj_load(rasz);

			_sprn->m_rasz = rasz;
			_sprn->bd_rasz = true;
		}
	}
}

//
void qgDevice::SetBlnd(qgBlnd* blnd)
{
	QGTRACE_DEVICE(qgDevice);

	kGuardSpinLock(_dlock)
	{
		if (_sprn->m_blnd != blnd)
		{
			kobj_unload(_sprn->m_blnd);
			kobj_load(blnd);

			_sprn->m_blnd = blnd;
			_sprn->bd_blnd = true;
		}
	}
}

//
void qgDevice::SetSplr(kint stage, qgSplr* splr)
{
	QGTRACE_DEVICE(qgDevice);

	k_return_if_fail((ksize_t)stage < (ksize_t)_caps.max_texture_count);

	kGuardSpinLock(_dlock)
	{
		if (_sprn->m_splr[stage] != splr)
		{
			kobj_unload(_sprn->m_splr[stage]);
			kobj_load(splr);

			_sprn->m_splr[stage] = splr;
			_sprn->bd_splr[stage] = true;
		}
	}
}

//
void qgDevice::SetShader(qgShader* shader)
{
	QGTRACE_DEVICE(qgDevice);

	kGuardSpinLock(_dlock)
	{
		if (_sprn->m_shd != shader)
		{
			kobj_unload(_sprn->m_shd);
			kobj_load(shader);

			_sprn->m_shd = shader;
			_sprn->bd_shd = true;
		}
	}
}

//
bool qgDevice::SetBuffer(qgBuffer* buffer)
{
	QGTRACE_DEVICE(qgDevice);

	if (!buffer)
	{
		kGuardSpinLock(_dlock)
		{
			for (kint i = 0; i < QGSGVERT_MAX_VALUE; i++)
			{
				if (_sprn->w_vb[i])
				{
					_sprn->w_vb[i]->Unload();
					_sprn->w_vb[i] = NULL;
					_sprn->bd_vb[i] = true;
				}
			}

			if (_sprn->w_ib)
			{
				_sprn->w_ib->Unload();
				_sprn->w_ib = NULL;
				_sprn->w_ioff = 0;
				_sprn->bd_ib = true;
			}
		}

		return true;
	}
	else
	{
		qgBufferType type = buffer->GetBufferType();

		if (type == QGBUFFER_VERTEX)
			return SetVertexBuffer(0, buffer);
		else if (type == QGBUFFER_INDEX)
			return SetIndexBuffer(0, buffer);
		else
		{
			// 이건 멀까요...
			kobj_tracep(KLV_ERROR, KERR_INVAL);
			return false;
		}
	}
}

//
bool qgDevice::SetVertexBuffer(kint stage, qgBuffer* buffer)
{
	QGTRACE_DEVICE(qgDevice);

	if ((ksize_t)stage >= QGSGVERT_MAX_VALUE)
	{
		// 범위를 넘어섬
		kobj_tracep(KLV_ERROR, KERR_RANGE);
		return false;
	}

	if (buffer)
	{
		if (buffer->GetBufferType() != QGBUFFER_VERTEX)
		{
			// 버퍼가 꼭지점이 아님
			kobj_tracep(KLV_ERROR, KERR_INVAL);
			return false;
		}

		kGuardSpinLock(_dlock)
		{
			if (_sprn->w_vb[stage] != buffer)
			{
				kobj_unload(_sprn->w_vb[stage]);
				buffer->Load();

				_sprn->w_vb[stage] = buffer;
				_sprn->bd_vb[stage] = true;
			}
		}
	}
	else
	{
		kGuardSpinLock(_dlock)
		{
			if (_sprn->w_vb[stage])
			{
				_sprn->w_vb[stage]->Unload();
				_sprn->w_vb[stage] = NULL;
				_sprn->bd_vb[stage] = true;
			}
		}
	}

	return true;
}

//
bool qgDevice::SetIndexBuffer(kint offset, qgBuffer* buffer)
{
	QGTRACE_DEVICE(qgDevice);

	if (buffer)
	{
		if (buffer->GetBufferType() != QGBUFFER_INDEX)
		{
			// 버퍼가 인덱스가 아님
			kobj_tracep(KLV_ERROR, KERR_INVAL);
			return false;
		}

		kGuardSpinLock(_dlock)
		{
			if (_sprn->w_ioff != offset)
			{
				_sprn->w_ioff = offset;
				_sprn->bd_ib = true;
			}

			if (_sprn->w_ib != buffer)
			{
				kobj_unload(_sprn->w_ib);
				buffer->Load();

				_sprn->w_ib = buffer;
				_sprn->bd_ib = true;
			}
		}
	}
	else
	{
		kGuardSpinLock(_dlock)
		{
			if (_sprn->w_ib)
			{
				_sprn->w_ib->Unload();
				_sprn->w_ib = NULL;
				_sprn->w_ioff = 0;
				_sprn->bd_ib = true;
			}
		}
	}

	return true;
}

//
bool qgDevice::SetLayout(qgLayout* lo)
{
	QGTRACE_DEVICE(qgDevice);

	if (lo)
	{
		kGuardSpinLock(_dlock)
		{
			if (_sprn->w_lo != lo)
			{
				// 세이더 없이도 그릴 수 있는 놈들이 있다... GL, DX9
				kobj_unload(_sprn->w_lo);
				lo->Load();

				_sprn->w_lo = lo;
				_sprn->bd_lo = true;
			}
		}
	}
	else
	{
		kGuardSpinLock(_dlock)
		{
			if (_sprn->w_lo)
			{
				_sprn->w_lo->Unload();
				_sprn->w_lo = NULL;
				_sprn->bd_lo = true;
			}
		}
	}

	return true;
}

//
void qgDevice::SetTopology(qgTopology tpg)
{
	QGTRACE_DEVICE(qgDevice);

	kGuardSpinLock(_dlock)
	{
		if (_sprn->w_tpg != tpg)
		{
			_sprn->w_tpg = tpg;
			_sprn->bd_tpg = true;
		}
	}
}

//
bool qgDevice::SetTM(qgTmType tm, const tpl::Mat4& m)
{
	QGTRACE_DEVICE(qgDevice);

	kGuardSpinLock(_dlock)
	{
		switch (tm)
		{
			case QGTMS_WORLD:
				_trfm.tm[QGTMS_WORLD] = m;
				break;

			case QGTMS_VIEW:
				_trfm.invv = m.Inverse();
				_trfm.vipr = m * _trfm.tm[QGTMS_PROJ];
				_trfm.tm[QGTMS_VIEW] = m;
				break;

			case QGTMS_PROJ:
				_trfm.vipr = _trfm.tm[QGTMS_VIEW] * m;
				_trfm.tm[QGTMS_PROJ] = m;
				break;

			default:
				if (tm >= QGTMS_MAX_VALUE)
					return false;
				else
				{
					//kint n = (kint)(tm - QGTMS_TEX_0);
					_trfm.tm[tm] = m;
				}
				break;
		}
	}

	return true;
}

//
void qgDevice::Draw(kint vertexcount, kint vertexstart)
{
	QGTRACE_DEVICE(qgDevice);
}

//
void qgDevice::DrawIndexed(kint indexcount, kint vertexstart)
{
	QGTRACE_DEVICE(qgDevice);
}

//
void qgDevice::DrawEnterIndirect(kint vertexcount, kint vertexstride, kpointer* outdata)
{
	QGTRACE_DEVICE(qgDevice);
}

//
void qgDevice::DrawLeaveIndirect()
{
	QGTRACE_DEVICE(qgDevice);
}

//
void qgDevice::DrawRect(const tpl::Rect& bound, const tpl::ColorValue& color, qgTexture* tex, const tpl::Quad* coord)
{
	QGTRACE_DEVICE(qgDevice);
}

//
void qgDevice::DrawGlyph(const tpl::Rect& bound, const tpl::ColorValue& color, qgTexture* tex)
{
	QGTRACE_DEVICE(qgDevice);
}

//
void qgDevice::DrawPolyLine(kint count, const tpl::Point* pts, const tpl::ColorValue* colors)
{
	QGTRACE_DEVICE(qgDevice);
}

//
void qgDevice::DrawPolyStrip(kint count, const tpl::Point* pts, const tpl::ColorValue* colors, qgTexture* tex, const tpl::Vec2* coords)
{
	QGTRACE_DEVICE(qgDevice);
}

//
kint qgDevice::_AcquireRenderTarget(qgTexture* tex)
{
	QGTRACE_DEVICE(qgDevice);

	k_return_value_if_fail(tex, -1);

	kSpinLock::ScopedLock lock(_dlock);

	kint i, found;

	for (found = -1, i = 0; i < QGSGOFF_MAX_VALUE; i++)
	{
		if (!_sprn->mo_tex[i])
		{
			found = i;
			break;
		}
	}

	if (found < 0)
		return -1;

	_sprn->mo_cnt++;
	_sprn->mo_tex[found] = tex;

	return found;
}

//
kint qgDevice::_ReleaseRenderTarget(qgTexture* tex)
{
	QGTRACE_DEVICE(qgDevice);

	k_return_value_if_fail(tex, -1);

	kSpinLock::ScopedLock lock(_dlock);

	kint i, found;

	for (found = -1, i = 0; i < QGSGOFF_MAX_VALUE; i++)
	{
		if (_sprn->mo_tex[i] == tex)
		{
			found = i;
			break;
		}
	}

	if (found < 0)
		return -1;

	_sprn->mo_cnt--;
	_sprn->mo_tex[found] = NULL;

	return found;
}

//
bool qgDevice::GetSpecific(qgRenderSpecific nth, kint* ret)
{
	k_return_value_if_fail(ret, false);

	kGuardSpinLock(_mlock)
	{
		switch (nth)
		{
			case QGRSPCF_DRAW_2D_AUTO_SET:
				*ret = (kint)_spcf.draw_2d_auto_set;
				break;

			default:
				return false;
		}
	}

	return true;
}

//
bool qgDevice::SetSpecific(qgRenderSpecific nth, kint value)
{
	kGuardSpinLock(_mlock)
	{
		switch (nth)
		{
			case QGRSPCF_DRAW_2D_AUTO_SET:
				_spcf.draw_2d_auto_set = value != 0;
				break;

			default:
				return false;
		}
	}

	return true;
}

//
void qgDevice::SetPropVec(kint n, const tpl::Vec4& v)
{
	k_return_if_fail((ksize_t)n < 4);
	_sdeb->propv[n] = v;
}

//
void qgDevice::SetPropMat(kint n, const tpl::Mat4& m)
{
	k_return_if_fail((ksize_t)n < 4);
	_sdeb->propm[n] = m;
}

//
void qgDevice::SetMatPalette(kint cnt, const tpl::Mat4* pcb)
{
	_sprn->wb_cnt = cnt;
	_sprn->wb_pcb = pcb;
}

//
qgBuffer* qgDevice::CreateBuffer(qgBufferType type, kint stride, kint count, kint flags, kconstpointer data)
{
	if (data)
		flags |= QGRESF_INIT;

	qgArprBuffer ap =
	{
		type, flags,
		stride, count,
		data
	};

	qgBuffer* base = _RoamImplBuffer(ap);

	if (base)
		_RoamLoad(QGROAM_OBJ_1, base);

	return base;
}

//
qgBuffer* qgDevice::CreateIndexBuffer(kint count, kint flags, bool is32bit)
{
	return CreateBuffer(QGBUFFER_INDEX, is32bit ? sizeof(kuint) : sizeof(kushort), count, flags, NULL);
}

//
qgBuffer* qgDevice::CreateIndexBuffer(const kuint* data, kint count, kint flags)
{
	return CreateBuffer(QGBUFFER_INDEX, sizeof(kuint), count, flags, data);
}

//
qgBuffer* qgDevice::CreateIndexBuffer(const kushort* data, kint count, kint flags)
{
	return CreateBuffer(QGBUFFER_INDEX, sizeof(kushort), count, flags, data);
}

//
qgBuffer* qgDevice::CreateVertexBuffer(kint count, qgLayout* lo, kint stage, kint flags)
{
	return CreateBuffer(QGBUFFER_VERTEX, lo->GetStageStride(stage), count, flags, NULL);
}

//
qgBuffer* qgDevice::CreateVertexBuffer(kconstpointer data, kint count, qgLayout* lo, kint stage, kint flags)
{
	return CreateBuffer(QGBUFFER_VERTEX, lo->GetStageStride(stage), count, flags, data);
}

//
qgLayout* qgDevice::CreateLayout(kint count, const qgLayoutElement* elms)
{
	k_return_value_if_fail(count > 0 && elms, NULL);

	qgLayoutData* data = qgLayout::_VerifyElements(count, elms);

	qgLayout* base = (qgLayout*)_RoamFind(QGROAM_RES_4, data);

	if (base)
		base->Load();
	else
	{
		base = _RoamImplLayout(data);

		if (base)
			_RoamLoad(QGROAM_RES_4, base);
	}

	delete data;

	return base;
}

//
qgTexture* qgDevice::CreateTexture(const char* name, kint width, kint height, kint depth, kint mips, kint flags, qgColorFormat fmt)
{
	qgTexture* base = name ? (qgTexture*)_RoamFind(QGROAM_RES_5, &name) : NULL;

	if (base)
		base->Load();
	else
	{
		qgArprTexture ap =
		{
			name, flags,
			QGTEXTURE_2D, fmt,
			width < 1 ? 1 : width,
			height < 1 ? 1 : height,
			depth < 1 ? 1 : depth,
			mips
		};

		if (ap.height > 1 && ap.depth > 1)
			ap.type = QGTEXTURE_3D;
		else if (ap.height == 1)
		{
			if (ap.depth == 6)
				ap.type = QGTEXTURE_CUBE;
			else
				ap.type = QGTEXTURE_1D;
		}

		base = _RoamImplTexture(ap);

		if (base)
			_RoamLoad(QGROAM_RES_5, base);
	}

	return base;
}

//
qgTexture* qgDevice::CreateTexture(const char* name, qgImage* img, kint flags)
{
	qgTexture* base = name ? (qgTexture*)_RoamFind(QGROAM_RES_5, &name) : NULL;

	if (base)
		base->Load();
	else
	{
		if (!img)
			return NULL;

		qgArprImage ap =
		{
			name, flags,
			img,
		};

		base = _RoamImplTexture(ap);

		if (base)
			_RoamLoad(QGROAM_RES_5, base);
	}

	return base;
}

//
qgTexture* qgDevice::CreateTexture(const char* name, kconstpointer data, kint size, kint flags)
{
	qgTexture* base = name ? (qgTexture*)_RoamFind(QGROAM_RES_5, &name) : NULL;

	if (base)
		base->Load();
	else
	{
		k_return_value_if_fail(data && size > 0, NULL);

		qgImage* img = kobj_new(qgImage);

		if (!_stub->PerformRead(img, data, size, _caps.flag_texture_image, QGSLCS_1))
			base = NULL;
		else
		{
			qgArprImage ap =
			{
				name, flags,
				img,
			};

			base = _RoamImplTexture(ap);

			if (base)
				_RoamLoad(QGROAM_RES_5, base);
		}

		img->Unload();
	}

	return base;
}

//
qgTexture* qgDevice::CreateTexture(const char* filename, kint flags)
{
	if (!filename)
		return NULL;

	qgTexture* base = (qgTexture*)_RoamFind(QGROAM_RES_5, &filename);

	if (base)
		base->Load();
	else
	{
		char* psz = k_strrchr(filename, '.');
		if (psz && k_stricmp(psz + 1, "HXP") == 0)
		{
			// 모션 애니메이션
			base = _RoamImplMotionTexture(filename, flags);

			if (base)
				_RoamLoad(QGROAM_RES_5, base);
		}
		else
		{
			// 파일에서 읽기
			kint readsize;
			kpointer readdata;

			if ((readdata = FileRead(filename, &readsize)) == NULL)
				base = NULL;
			else
			{
				qgImage* img = kobj_new(qgImage);

				if (!_stub->PerformRead(img, readdata, readsize, _caps.flag_texture_image, QGSLCS_1))
					base = NULL;
				else
				{
					qgArprImage ap =
					{
						filename, flags,
						img,
					};

					base = _RoamImplTexture(ap);

					if (base)
						_RoamLoad(QGROAM_RES_5, base);
				}

				img->Unload();
				k_delete(readdata);
			}
		}
	}

	return base;
}

//
qgDepthBuffer* qgDevice::CreateDepthBuffer(kint width, kint height)
{
	tpl::Point pt(width, height);

	qgDepthBuffer* base = (qgDepthBuffer*)_RoamFind(QGROAM_RES_6, &pt);

	if (base)
		base->Load();
	else
	{
		base = _RoamImplDepthBuffer(pt);

		if (base)
			_RoamLoad(QGROAM_RES_6, base);
	}

	return base;
}

//
qgShader* qgDevice::CreateShader(const char* name)
{
	qgShader* base;

	if (name)
	{
		qgShader::KeyType key(name);

		base = (qgShader*)_RoamFind(QGROAM_MTL_2, &key);
		if (base)
		{
			base->Load();
			return base;
		}
	}

	base = _RoamImplShader(name);

	if (base)
		_RoamLoad(QGROAM_MTL_2, base);

	return base;
}

//
qgRasz* qgDevice::CreateRasz(const qgRaszData& data)
{
	qgRasz* base = (qgRasz*)_RoamFind(QGROAM_RES_1, &data);

	if (base)
		base->Load();
	else
	{
		base = _RoamImplRasz(data);

		if (base)
			_RoamLoad(QGROAM_RES_1, base);
	}

	return base;
}

//
qgBlnd* qgDevice::CreateBlnd(const qgBlndData& data)
{
	qgBlnd* base = (qgBlnd*)_RoamFind(QGROAM_RES_2, &data);

	if (base)
		base->Load();
	else
	{
		base = _RoamImplBlnd(data);

		if (base)
			_RoamLoad(QGROAM_RES_2, base);
	}

	return base;
}

//
qgSplr* qgDevice::CreateSplr(const qgSplrData& data)
{
	qgSplr* base = (qgSplr*)_RoamFind(QGROAM_RES_3, &data);

	if (base)
		base->Load();
	else
	{
		base = _RoamImplSplr(data);

		if (base)
			_RoamLoad(QGROAM_RES_3, base);
	}

	return base;
}

